(import
 (except (rnrs base) let-values map)
 (only (guile)
       lambda* λ
       simple-format
       display)
 (stream))


;; Define abstraction for fractions.
(define numerator
  (λ (fraction)
    (car fraction)))


(define denominator
  (λ (fraction)
    (cdr fraction)))


(define make-fraction
  (λ (num denom)
    (cons num denom)))


;; natural numbers

(define natural-numbers
  (make-stream 0 0 0
               (λ (val index prev-input) (+ prev-input 1))
               (λ (x) x)))


(display
 (simple-format
  #f "third natural number: ~a\n" (stream-get-nth-value natural-numbers 3)))


;; If we have a function calculating a next value from a
;; previous value, it is easy to use that function for
;; creating a stream, as we will see in the example of
;; fractions.
(define next-rational-fraction
  (λ (fraction)
    (let ([num (numerator fraction)]
          [denom (denominator fraction)])
      (cond [(= num 1)
             (if (odd? denom)
                 (make-fraction num
                                (+ denom 1))
                 (make-fraction (+ num 1)
                                (- denom 1)))]
            [(= denom 1)
             (if (odd? num)
                 (make-fraction (- num 1)
                                (+ denom 1))
                 (make-fraction (+ num 1)
                                denom))]
            [else (if (odd? (+ num denom))
                      (make-fraction (+ num 1)
                                     (- denom 1))
                      (make-fraction (- num 1)
                                     (+ denom 1)))]))))

;; Build a stream using next-rational-fraction.
(define rational-numbers
  (make-stream (make-fraction 1 1)
               0
               0
               (λ (val index prev-input) val)
               next-rational-fraction))


(display
 (simple-format
  #f "fourth fraction: ~a\n" (stream-get-nth-value rational-numbers 5)))

(display
 (simple-format
  #f "position of 7/8 in the fractions stream: ~a\n"
  (stream-pos (make-fraction 7 8) rational-numbers)))

(display
 (simple-format
  #f "97. fraction: ~a\n"
  (stream-get-nth-value rational-numbers 97)))


(display
 (simple-format
  #f "double of third natural number: ~a\n"
  (let ([double-natural-numbers-stream
         (stream-map (λ (x) (* x 2))
                     natural-numbers)])
    (stream-get-nth-value double-natural-numbers-stream 3))))

(display
 (simple-format
  #f "double of hundredth natural number: ~a\n"
  (let ([double-natural-numbers-stream
         (stream-map (λ (x) (* x 2))
                     natural-numbers)])
    (stream-get-nth-value double-natural-numbers-stream 100))))


(display
 (simple-format
  #f "111. square natural number: ~a\n"
  (let ([square-natural-numbers-stream
         (stream-map (λ (x) (* x x))
                     natural-numbers)])
    (stream-get-nth-value square-natural-numbers-stream 111))))

(display
 (simple-format
  #f "first 20 square numbers: ~a\n"
  (let ([square-natural-numbers-stream
         (stream-map (λ (x) (* x x))
                     natural-numbers)])
    (stream->list square-natural-numbers-stream 20))))
